dojo.provide("lorelei.mml.composer");

// Lorelei MML Composer Script
// Copyright (c)2008 Logue <http://logue.be/> All rights reserved.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// based on Unofficial Composing Tool.<http://kiyuyume.gusoku.net/garakuta/composer.html>.

lorelei.mml.composer = {
	meta : {
		"@prefix": "<http://purl.org/net/ns/doas#>",
		"@about": "<http://mabinogi.logue.be/js/lorelei/mml/composer.js>", a: ":JavaScript",
		title: "Lorelei MML Composer Script",
		shortdesc: "Lorelei MML Web Composer Tool Script.",
		created: "2008-06-05", release: {revision: "1.7.5", created: "2008-10-09"},
		author: {name: "Logue", homepage: "<http://logue.be/>"},
		license: "<http://www.gnu.org/licenses/gpl-3.0.html>",
		acknowledgement: {name: "Kiyoyume", 'homepage': "<http://kiyuyume.gusoku.net/>" },
		dependencies: "dojo.js"
	},
	init : function(){
		// 初期値
		this.setLength(4);
		this.cookie('load');
//		this.setBack('setup');

		var composer = this;

		// テンポ変更
		dojo.connect(dojo.byId('ApplyTempo'),'onclick',function(){
			var Tempo = dojo.byId('tempo') - 0;
			if (32 <= Tempo || Tempo <= 255){
				composer.writeData( 'T'+Tempo );
			}else{
				alert(message['tempo']);
			}
		});

		// ボリューム変更
		dojo.connect(dojo.byId('ApplyVolume'),'onclick',function(){
			var Volume = dojo.byId('volume').value - 0;
			if( 0 <= Volume || Volume <= 15) {
				composer.writeData( 'V'+Volume );
			}else{
				alert(message['volume']);
			}
		});

		// 音長ボタン
		dojo.query('button.NoteLength').connect('onclick',function(item){
			var id = item.target.id.replace("_text", "");
			composer.setLength(id.replace("note_", ""));
		});

		// 休符ボタン
		dojo.query('button.Rest').connect('onclick',function(item){
			var id = item.target.id.replace("_text", "");
			composer.writeData( 'R'+id.replace("rest_", "") );
		});

		// タイ記号
		dojo.connect(dojo.byId('tie'),'onclick',function(){
			composer.writeData("&");
		});

		// 音符挿入
		dojo.query('div#composer-keyboard a').connect('onclick',function(elem){
			var id = elem.target.id.replace("_text", "");
			composer.setNote(id.replace("s", "+"));
		});

		// ハイライト
		dojo.query('div#composer-keyboard a').connect('onmouseover',function(elem){
			var id = elem.target.id.replace("_text", "");
			var color = '#FF9';	//通常の鍵盤の場合黄色くハイライト
			if (id.match('s$')) color = '#CCF';	// 黒い鍵盤の場合、青くハイライト
			var highlight = dojo.animateProperty({
				node: elem.target,
				duration: 750,
				properties: {
					backgroundColor: { start:color,end:'transparent'},
					opacity:{start:1,end:0}
				}
			});
			highlight.play();
		});
	},
	setLength : function(value){
		// 音長指定
//		console.log(value)
		dojo.byId('length').value = value;

		dojo.query('button.NoteLength').forEach(function(length) {
			var id = length.id.replace("_text", "");

			length.style.borderColor = "#dedede";
			length.style.backgroundColor = "#fff";
			if (value == length.id.replace("note_", "")){
				length.style.borderColor = "#FCC";
				length.style.backgroundColor = "#FEE";
			}
		});
		dojo.query('button.Rest').forEach(function(rest) {
			var id = rest.id.replace("_text", "");
			rest.style.borderColor = "#dedede";
			rest.style.backgroundColor = "#fff";
			if (value == rest.id.replace("rest_", "")){
				rest.style.borderColor = "#CFF";
				rest.style.backgroundColor = "#EFF";
			}
		});
	},
	setNote : function(value){
		// ノート入力
		var MML = this.getData();
		
		var output = '';
		Timebase  = 96;
		CurrentLength = dojo.byId('length').value;
		CurrentOctave = dojo.byId('octave').value;
		var isDotted = nowOctave = nowLength = false;

		// 選択されているMMLデータを得る
		var mmls = this.analyze(MML);

		// MMLデータを解析し、それを元にオクターブと基本音の長さを求める
		for(i=0; i<mmls.length; i++) {
			switch (mmls[i][0]) {
				case 'L': case 'l':
					nowLength = mmls[i][1];
					isDotted  = mmls[i][2];
				break;

				case 'O': case 'o':
					nowOctave = mmls[i][1];
				break;

				case '>':
					if(nowOctave == false) {
						nowOctave = CurrentOctave + 1;
					} else {
						nowOctave++;
					}
				break;

				case '<':
					if(nowOctave == false) {
						nowOctave = CurrentOctave - 1;
					} else {
						nowOctave--;
					}
				break;
			}
		} // End Of For

		if (nowLength == false) nowVolume = 4;	// 基本音の長さが指定されていない場合

		// 現在のオクターブとの変位により処理を変える
		if(nowOctave == false){
			nowOctave = 4;
			output += "o4";	// デフォルト値
		}else if(1 <= nowOctave || nowOctave <= 8) {
			if(CurrentOctave == nowOctave) {
					// 現在のオクターブと同じ
			}else if(CurrentOctave == nowOctave + 1) {	// ＋１
				output += ">";
			}else if(CurrentOctave == nowOctave - 1) {	// －１
				output += "<";
			}else {
				// 変位が±２以上
				output += "o" + nowOctave;
			}
		}else{
			alert(message['octave']);
		}

		// 音の長さの変化を調べる
		var cDotted = dijit.byId('dotted').checked;
		var cflg = false;	// ChangedFlag

		if(CurrentLength != nowLength) {
			nowLength = CurrentLength;
			cflg = true;
		}
		if(cDotted != isDotted) {
			isDotted = cDotted;
			cflg = true;
		}

		if(cflg){
			// 音の長さに変化があった場合
			if (Timebase*2 % nowLength == 0){
				output += "l" + nowLength + (isDotted ? "." : "");
			}else{
				alert(message['length']);
			}
		}

		// 音符の挿入
		this.writeData( output + value );
	},
	writeData : function ( str ) {
		var Tracks = dojo.query("input[type=radio][name=track]");

		// 渡された値を現在選択されているテキストエリアに書き込む
		for(i=0; i<Tracks.length; i++) {
			if (Tracks[i].checked) dojo.byId(Tracks[i].value).value += str;
		}

		this.setBack();
		lorelei.mml.player.setup();
		lorelei.mml.checker.check_rank();
	},
	getData : function(){
		var Tracks = dojo.query("input[type=radio][name=track]");
		for(i=0; i<Tracks.length; i++) {
			if (Tracks[i].checked) return dojo.byId(Tracks[i].value).value;
		}
	},
	analyze : function(str){
		// 与えられたString文をマビノギ用MMLデータとみなし、
		// 文字ごとに区切り２次元配列にして返す
		// 戻り値rtは
		//	rt[X][0]	文字
		//	rt[X][1]	数字
		//	rt[X][2]	true / false	付点の有無
		//	rt[X][3]	true / false	＃の有無
		//	rt[X][4]	引数strの何文字目か

//		console.log('analyzeMML... ',str);

		// 一時的に使用するための配列
		// 戻り値rtのうち[0],[1],[2],[4]のみ実装
		var tmpArray = [];	// 配列初期化
		
		// 解析
		var cnt = 0;
		var s, a, b;
		var cOctave = 4;
		for(i=0, j=-1; i<str.length+1; i++) {
			// 文字ごとに区切って値をexeFuncに渡していく
		    s = str.charAt(i);
			if(s=='0' || s=='1' || s=='2' || s=='3' || s=='4' || s=='5' ||
													s=='6' || s=='7' || s=='8' || s=='9') {
				// sが数字
				
			} else {
				// sが文字
				a = str.charAt(j)
				b = str.substring(j+1, i);
				switch (a) {
					// 休符
					case 'R': case 'r':
						tmpArray[cnt] = [a, parseInt(b), null, j];
					break;

					// 長さ
					case 'L': case 'l':
						if(str.charAt(i) == '.') {
							// 付点付き
							tmpArray[cnt] = [a, parseInt(b), true, j];
						} else {
							// ノーマル
							tmpArray[cnt] = [a, parseInt(b), false, j];
						}
					break;

					// オクターブ
					case 'O': case 'o':
						cOctave = parseInt(b);
						if(b>=1 && b<=8) tmpArray[cnt] = [ a, cOctave, null, j];
					break;

					// オクターブ＋
					case '>':
						cOctave = cOctave+1;
						tmpArray[cnt] = [a, null, null, j];
					break;

					// オクターブ－
					case '<':
						cOctave = cOctave-1;
						tmpArray[cnt] = [a, null, null, j];
					break;

				
					case 'T': case 't':	// テンポ
						if(b>=32 && b<=255) tmpArray[cnt] = [ a, parseInt(b), null, j];
					break;

					// ボリューム
					case 'V': case 'v':
						if(b>=0 && b<=15) tmpArray[cnt] = [ a, parseInt(b), null, j];
					break;

					// 絶対音階値指定（オクターブ＋ノート＋オクターブという形にする）
					case 'N': case 'n':
						b = parseInt(b);
						var Octave = Math.floor(b/12);	// ノート値を12で割った値がオクターブ
						var note = noteRevTable[b%12];	// あまりがノート値

						if (cOctave == Octave){
							tmpArray[cnt] = new Array( note, null, null, j);
						}else{
							if (cOctave == Octave+1){
								tmpArray[cnt] = [ '>', null, null, j];
								cnt++;
								tmpArray[cnt] = [note, null, null, j];
								cnt++;
								tmpArray[cnt] = ['<', null, null, j];
							}else if (cOctave == Octave-1){
								tmpArray[cnt] = ['<', null, null, j];
								cnt++;
								tmpArray[cnt] = [note, null, null, j];
								cnt++;
								tmpArray[cnt] = ['>', null, null, j];
							}else{
								tmpArray[cnt] = ['O', octave, null, j];
								cnt++;
								tmpArray[cnt] = [note, null, null, j];
								cnt++;
								tmpArray[cnt] = ['O', cOctave, null, j];
							}
						}
					break;

					// 音符
					default:
						if(b=="") {
							tmpArray[cnt] = new Array( a, null, null, j);
						} else if(str.charAt(i) == '.' && b != '') {
							// 付点音符の場合
							tmpArray[cnt] = [a, parseInt(b), true, j];
						} else {
							// 通常音符の場合
							tmpArray[cnt] = [a, parseInt(b), false, j];
						}
					break;
				}
				cnt++;
				j = i;
			}
		} // End Of For
		
		// 戻り値用に配列の長さ調整し、＃の有無の情報を加える
		var rt = new Array(cnt);
		for(i=0; i<cnt; i++) {
			if(i<cnt-1 && tmpArray[i+1][0] == '#') {	// 次の文字が#
				rt[i] = [tmpArray[i][0], tmpArray[i+1][1], tmpArray[i+1][2], true, tmpArray[i][3]];
			} else {
				rt[i] = [tmpArray[i][0], tmpArray[i][1], tmpArray[i][2], false, tmpArray[i][3]];
			}
		}
		return rt;
	},
	cookie: function(mode){
		if(mode == 'load'){
			dojo.require("dojo.cookie");
			if(dojo.cookie('composer.ch1')) dojo.byId('ch1').value = dojo.cookie('composer.ch1');
			if(dojo.cookie('composer.ch2')) dojo.byId('ch2').value = dojo.cookie('composer.ch2');
			if(dojo.cookie('composer.ch3')) dojo.byId('ch3').value = dojo.cookie('composer.ch3');
			if(dojo.cookie('composer.inst')) dijit.byId('instrument').value = dojo.cookie('composer.inst');
			lorelei.mml.checker.check_rank();
		}else if(mode == 'dele'){
			dojo.cookie('composer.ch1',null);
			dojo.cookie('composer.ch2',null);
			dojo.cookie('composer.ch3',null);
			dojo.cookie('composer.inst',null);
		}else{
//			if(window.confirm('Save to Cookie?')){
				dojo.cookie('composer.ch1',dojo.byId('ch1').value);
				dojo.cookie('composer.ch2',dojo.byId('ch2').value);
				dojo.cookie('composer.ch3',dojo.byId('ch3').value);
				dojo.cookie('composer.inst',lorelei.mml.checker.inst);
//			}
		}
	},
	setBack: function(mode){
		if (mode == 'setup'){
			dojo.require("dojo.back");

			var scriptTag = document.createElement('script');
			scriptTag.type = 'text/javascript';
			scriptTag.defer = 'defer';
			scriptTag.text = 'dojo.back.init();';
			dojo.body().appendChild(scriptTag);

			// ブラウザの「戻る」「進む」ボタンをハッキング
			ApplicationState = function(ch1,ch2,ch3){	//Line350
				this.changeUrl = false;
				this.ch1 = ch1;
				this.ch2 = ch2;
				this.ch3 = ch3;
			};

			dojo.extend(ApplicationState, {
				// 「戻る」ボタンによる取り消し
				back: function(){
					dojo.byId('ch1').value = this.ch1;
					dojo.byId('ch2').value = this.ch2;
					dojo.byId('ch3').value = this.ch3;
				},
				// 「進む」ボタンによるやり直し
				forward: function(){
					dojo.byId('ch1').value = this.ch1;
					dojo.byId('ch2').value = this.ch2;
					dojo.byId('ch3').value = this.ch3;
				}
			});
			dojo.back.setInitialState();
		}else if(dojo.back){
			var appState = new ApplicationState(dojo.byId('ch1').value, dojo.byId('ch2').value, dojo.byId('ch3').value);
			dojo.back.addToHistory(appState);
		}
	}
};

console.log(lorelei.mml.composer.meta.title,lorelei.mml.composer.meta.release,' has been loaded!');
dojo.addOnLoad(function (){
	lorelei.mml.composer.init();
});

dojo.addOnUnload(function(){
	lorelei.mml.composer.cookie();
});